import 'package:json_format/common/models/model.dart';
import 'package:json_format/common/utils/covert.util.dart';

class ModelDartConvert {
  /// \n
  static String wrap = '\n';

  /// \t
  static String space = "    ";

  static bool isHasDecimal = false;
  static bool isHasMoment = false;

  static String parseModelV2(InnerModel model, {bool hasToJson = true}) {
    String output = '';
    output += wrap;
    // 嵌套的模型
    List<InnerModel> nestModels = [];
    if (model.propertys?.isNotEmpty == true) {
      output += 'class ${model.name} {';
      for (var property in model.propertys!) {
        if (property.subModel != null) {
          nestModels.add(property.subModel!);
        }
        output += wrap;
        String writePro = parsePropertyV2(property);
        output += writePro;
      }
      output += wrap * 2;
      // constructor api
      output += parseConstructorV2(model);
      // json api
      output += wrap * 2;
      output += parseFromJsonApiV2(model);
      output += wrap * 2;
      if(hasToJson){
        output += parseToJsonApi(model);
        output += wrap;
      }
      output += '}';
    }

    if (nestModels.isNotEmpty) {
      for (var model in nestModels) {
        output += wrap * 2;
        output += parseModelV2(model,hasToJson: hasToJson);
      }
    }
    return output;
  }

  static String parsePropertyV2(InnerProperty property) {
    String output = '';
    String name = property.name.replaceAll("_", "");
    switch (property.type) {
      case InnerType.bool:
        {
          output += space;
          output += 'bool $name;';
        }
        break;
      case InnerType.int:
        {
          output += space;
          output += 'int $name;';
        }
        break;
      case InnerType.moment:
        {
          output += space;
          output += 'Moment? $name;';
          isHasMoment = true;
        }
        break;
      case InnerType.decimal:
        {
          output += space;
          output += 'Decimal $name;';
          isHasDecimal = true;
        }
        break;
      case InnerType.string:
        {
          output += space;
          output += 'String $name;';
        }
        break;
      case InnerType.list:
        {
          output += space;
          List arr = property.value;
          String nameT = '';
          if (arr.isNotEmpty) {
            dynamic first = arr.first;
            String t;
            if (property.subModel != null) {
              t = property.subModel!.name;
            } else {
              t = arr.first.runtimeType.toString();
            }
            dynamic inner = first;
            while (inner is List) {
              t = 'List<$t>';
              if (inner.isNotEmpty) {
                inner = inner.first;
              }
            }
            nameT = '<$t>';
          } else {
            nameT = '<dynamic>';
          }
          output += 'List$nameT $name;';
        }
        break;
      case InnerType.object:
        {
          output += space;
          InnerModel model = property.value;
          if (model.propertys?.isNotEmpty == true) {
            output += '${model.name} $name;';
          } else {
            output += 'dynamic $name;';
          }
        }
        break;
      case InnerType.nil:
        {
          output += space;
          output += "dynamic $name;";
        }
        break;
      default:
        break;
    }
    return output;
  }

  static String parseConstructorV2(InnerModel model) {
    String output = '';
    if (model.propertys != null) {
      for (var property in model.propertys!) {
        String name = property.name.replaceAll("_", "");
        output += "required this.$name,";
      }
    }
    return '${model.name}({ $output});';
  }

  static String parseFromJsonApiV2(InnerModel model) {
    String output =
        'factory ${model.name}.fromMap(Map<String, dynamic> json) {';
    output += wrap;
    output += '${space}return ${model.name}(';
    if (model.propertys != null) {
      for (var property in model.propertys!) {
        output += wrap;
        output += space;
        String name = property.name.replaceAll("_", "");
        if (property.type == InnerType.object) {
          assert(property.subModel != null);
          if (property.subModel?.propertys?.isNotEmpty == true) {
            output +=
                "$name:${property.subModel?.name}.fromMap(json['${property.name}']),";
          } else {
            output += "$name:json['${property.name}'],";
          }
        } else if (property.type == InnerType.list) {
          List list = property.value;
          if (property.subModel != null) {
            if (list.isNotEmpty) {
              output +=
                  "$name : ((json['${property.name}'] ?? []) as List<dynamic>)";
              output += '.map((e) => ';
              dynamic first = list.first;
              String append = '';
              while (first is List) {
                output += '(e as List<dynamic>).map((e) => ';
                append += ').toList()';
                first = first.first;
              }
              if (first is InnerModel) {
                output += '${first.name}.fromMap(e)';
              }
              output += append;
              output += ').toList(),';
            }
          } else {
            if (list.isEmpty) {
              output += property.dynamicData;
            } else {
              output += property.valueTypeList;
            }
          }
        } else {
          switch (property.type) {
            case InnerType.moment:
              {
                output +=
                    "$name : json['${property.name}'] == null?null:Moment.tryParse(json['${property.name}'])";
              }
              break;
            case InnerType.decimal:
              {
                output +=
                    "$name : json['${property.name}'] == null?Decimal.zero:Decimal.tryParse(json['${property.name}'])??Decimal.zero";
              }
              break;
            default:
              output += "$name : json['${property.name}']";
              switch (property.type) {
                case InnerType.bool:
                  {
                    output += ' ?? false';
                  }
                  break;
                case InnerType.int:
                  {
                    output += ' ?? 0';
                  }
                  break;
                case InnerType.string:
                  {
                    output += " ?? ''";
                  }
                  break;
                case InnerType.list:
                  {
                    output += ' ?? []';
                  }
                  break;
                default:
                  break;
              }
              break;
          }
          output += ',';
        }
      }
    }
    output += wrap;
    output += ');';
    output += wrap;
    output += '}';
    return output;
  }

  static String parseToJsonApi(InnerModel model) {
    String output = 'Map<String, dynamic> toJson() {';
    output += wrap;
    output += 'return {';
    if (model.propertys != null) {
      for (var property in model.propertys!) {
        output += wrap;
        output += space;
        String name = property.name.replaceAll("_", "");
        switch (property.type) {
          case InnerType.object:
            {
              if (property.subModel?.propertys?.isNotEmpty == true) {
                output +=
                    "'${property.name}': ${property.name}${property.optional ? '?' : ''}.toJson(),";
              } else {
                output += "'${property.name}': ${property.name},";
              }
            }
            break;
          case InnerType.list:
            {
              if (property.subModel != null) {
                output +=
                    "'${property.name}': ${property.name}${property.optional ? '?' : ''}.map((e) => ";
                List arr = property.value;
                if (arr.isNotEmpty) {
                  dynamic first = arr.first;
                  String append = '';
                  while (first is List) {
                    first = first.first;
                    output += 'e.map((e) => ';
                    append += ').toList()';
                  }
                  if (first is InnerModel) {
                    output += 'e.toJson()';
                  }
                  output += append;
                }
                output += ').toList(),';
              } else {
                output += "'${property.name}': $name,";
              }
            }
            break;
          default:
            {
              output += "'${property.name}': $name,";
            }
            break;
        }
      }
      output += wrap;
    }
    output += '};';
    output += wrap;
    output += '}';
    return output;
  }
}
